package com.pro.staffmanage.security.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.pro.staffmanage.resolvers.MyLocaleResolver;
import com.pro.staffmanage.security.filter.StaffManagerVerifyFilter;
import com.pro.staffmanage.pojo.User;
import com.pro.staffmanage.security.service.PersistentRememberMeServices;
import com.pro.staffmanage.service.UserService;
import com.pro.staffmanage.utils.DataBaseUtil;
import com.pro.staffmanage.utils.PasswordUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Configuration
@EnableWebSecurity
public class MySecurityConfig {

    private final UserService userService;
    private DataSource dataSource;
    private AuthenticationConfiguration authenticationConfiguration;

    @Autowired
    public MySecurityConfig(
            UserService userService
            , DataSource dataSource
            , AuthenticationManagerBuilder builder
            , AuthenticationConfiguration authenticationConfiguration
    ) throws Exception {

        this.userService = userService;
        this.dataSource = dataSource;
        this.authenticationConfiguration = authenticationConfiguration;
        builder.userDetailsService(this.userService);
    }

    /**
     * 用我们自己自定义的密码算法
     *
     * @return PasswordEncoder
     */
    @Bean
    public PasswordEncoder delegatingEncoderBean() {
        return PasswordUtil.createDelegatingPasswordEncoder();
    }

    @Bean
    public StaffManagerVerifyFilter verifyFilter() throws Exception {
        StaffManagerVerifyFilter verifyFilter = new StaffManagerVerifyFilter();
        verifyFilter.setUsernameParameter("username");
        verifyFilter.setPasswordParameter("password");
        verifyFilter.setVerifyCode("userVerifyCode");
        verifyFilter.setAuthenticationManager(authenticationConfiguration.getAuthenticationManager());
        verifyFilter.setFilterProcessesUrl("/doLogin");
        //  添加rememberMe这个是关键: 第一步添加rememberMe
        verifyFilter.setRememberMeServices(this.rememberMeServicesBean());
        verifyFilter.setAuthenticationSuccessHandler(
                (request, response, authentication) -> {
                    Map<String, Object> res = new HashMap<>(3);
                    res.put("msg", "登录成功");
                    res.put("status", 200);
                    res.put("authentication", authentication);
                    //  设置response格式为json, 字符编码为UTF-8
                    response.setContentType("application/json;charset=UTF-8");
                    //  将登录成功的数据以session的方式传送, 重定向到"localhost:8080/main"主页
                    request.getSession().setAttribute("loginSuccess", res);
                    response.sendRedirect("/main");
                }
        );
        verifyFilter.setAuthenticationFailureHandler(
                (request, response, exception) -> {
                    Map<String, Object> res = new HashMap<>(3);
                    res.put("msg", "登录失败");
                    res.put("status", 401);
                    res.put("exception", exception);
                    //  设置response格式为json, 字符编码为UTF-8
                    response.setContentType("application/json;charset=UTF-8");
                    //  将登陆失败数据发给401
                    request.getSession().setAttribute("loginFailure", res);
                    response.getWriter().println(new ObjectMapper().writeValueAsString(res));
                }
        );
        return verifyFilter;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.authorizeRequests(
                        authorize ->
                                authorize
                                        .mvcMatchers("/", "/login","/verify*","/index*","/swagger-ui/**").permitAll()
                                        .mvcMatchers("/main").hasAuthority("user")
                                        .mvcMatchers("/emp/**").hasAuthority("manager")
                                        .mvcMatchers("/dep/**").hasAuthority("boss")
                                        .anyRequest()
                                        .authenticated()
                )
                .addFilterAt(this.verifyFilter(), UsernamePasswordAuthenticationFilter.class)
                .formLogin().loginPage("/").loginProcessingUrl("/doLogin")
                .and()
                //  添加rememberMe第二部关键点: 存储和解码
                .rememberMe().rememberMeParameter("remember-me").rememberMeServices(this.rememberMeServicesBean())
                .and()
                .logout().logoutRequestMatcher(
                        new OrRequestMatcher(
                                new AntPathRequestMatcher("/logout", "GET")
                        )
                )
                .logoutSuccessHandler(
                        (request, response, authentication) -> {
                            Map<String, Object> res = new HashMap<>(3);
                            res.put("msg", "登出成功");
                            res.put("status", 200);
                            res.put("authentication", authentication);
                            userService.deleteUser(((User) userService.loadUserByUsername(authentication.getName())).getId());
                            response.setContentType("application/json;charset=UTF-8");
                            //  将登出成功数据转发到登录页(首页)
                            request.getSession().setAttribute("logoutSuccess", res);
                            response.sendRedirect("/login");
                        }
                )
                .and()
                .csrf().disable()
                .build();
    }

    @Bean
    public PersistentTokenRepository persistentTokenRepositoryBean() throws SQLException {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        //  数据源放进去, SQL语句他写好的(建表+增删改查)
        if (jdbcTokenRepository.getDataSource() == null){
            jdbcTokenRepository.setDataSource(dataSource);
        }
        //  不存在则创建validate
        if(!DataBaseUtil.validateTableNameExist("persistent_logins",jdbcTokenRepository.getDataSource())){
            jdbcTokenRepository.setCreateTableOnStartup(true);
        }
        return jdbcTokenRepository;
    }

    @Bean
    public RememberMeServices rememberMeServicesBean() throws SQLException {
        return new PersistentRememberMeServices(
                UUID.randomUUID().toString(),
                userService,
                persistentTokenRepositoryBean()
        );
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        //  忽视哪些
        return (web) -> web
                .ignoring()
                //  为了更便捷的处理静态资源, 一般用ant, 否则根据SpringBoot里的配置, static就是根目录了
                .mvcMatchers("/css/**", "/img/**", "/js/**", "/druid/**", "/reg", "/401", "/404");
    }

}
